﻿using System;
using System.Net;
using System.IO;

namespace CD2WebAudio3Lib
{
    public delegate void FTPProgressHandler(object pSender, decimal pPercentDone, string pMessage);

    public class file_to_publish
    {
        private string m_Description = string.Empty;
        private string m_FullPath = string.Empty;
        private string m_URL = string.Empty;
        private string m_Username = string.Empty;
        private string m_Password = string.Empty;
        private string m_Port = string.Empty;

        // number of times to try before giving up
        private int m_TotalAttempts = 11;
        private int m_CurrentAttempt = 0;
        private bool m_Finished = false;
        private int m_RetryFileExists = 1;

        public event FTPProgressHandler FTPProgress;

        public file_to_publish(string pDescription, string pFullPath, string pURL, string pUsername, string pPassword, string pPort)
        {
            m_Description = pDescription;
            m_FullPath = pFullPath;
            m_URL = pURL;
            m_Username = pUsername;
            m_Password = pPassword;
            m_Port = pPort;
        }

        public bool Publish()
        {
            // resolve the path
            FTPProgress(this, 0, "Resolving FTP server . . .");
            m_URL = resolve_ftp.Resolve_FTP_Address(m_URL, m_Username, m_Password, m_Port);

            // rename the file if it already exists
            while ((m_RetryFileExists > 0) && (m_RetryFileExists < 10))
            {
                if ((file_Exists()) && (m_RetryFileExists == 0))
                {
                    rename_File();
                }
            }

            long startAt = 0;
            while ((!m_Finished) && (m_CurrentAttempt < m_TotalAttempts))
            {
                // send to server
                if (try_To_Send(startAt))
                {
                    m_Finished = true;
                }
                else
                {
                    // pause
                    Wait(5);

                    // try again
                    m_CurrentAttempt++;

                    // how much was sent
                    if (file_Exists()) startAt = get_File_Length();
                    else startAt = 0;
                }
            }

            return m_Finished;
        }

        public string Description
        {
            get { return m_Description; }
        }

        private bool try_To_Send(long pStartAt)
        {
            FtpWebRequest request = (FtpWebRequest)WebRequest.Create(new Uri(build_URL()));
            if (pStartAt == 0)
            {
                request.Method = WebRequestMethods.Ftp.UploadFile;
            }
            else
            {
                request.Method = WebRequestMethods.Ftp.AppendFile;
            }

            FileInfo fi = new FileInfo(m_FullPath);
            byte[] fileContents;

            // read the file to upload
            using (FileStream sourceStream = new FileStream(m_FullPath, FileMode.Open))
            {
                BinaryReader binStream = new BinaryReader(sourceStream);
                fileContents = binStream.ReadBytes((int)fi.Length);
                sourceStream.Close();
            }

            System.Windows.Forms.Application.DoEvents();
            request.ContentLength = fileContents.Length;

            // set username and password
            request.Credentials = new NetworkCredential(m_Username, m_Password);

            //  changes for a more reliable connection
            request.KeepAlive = false;
            request.UseBinary = true;

            // starting position and size
            int i = (int)pStartAt;
            int size = 2048;

            // check for past end
            if ((i + size) > fileContents.Length) size = fileContents.Length - i;

            // message
            string message = string.Empty;
            if (m_CurrentAttempt > 0) message = "Retry #" + m_CurrentAttempt.ToString() + " for file " + fi.Name + " . . .";

            try
            {
                using (Stream requestStream = request.GetRequestStream())
                {
                    while (i < fileContents.Length)
                    {
                        requestStream.Write(fileContents, i, size);
                        System.Windows.Forms.Application.DoEvents();
                        FTPProgress(this, (decimal)((i + size) * 100 / fileContents.Length), message);
                        System.Windows.Forms.Application.DoEvents();
                        i += size;

                        // check for past end
                        if ((i + size) > fileContents.Length) size = fileContents.Length - i;
                    }
                    requestStream.Close();
                }
            }
            finally
            {
                System.Windows.Forms.Application.DoEvents();
                using (FtpWebResponse response = (FtpWebResponse)request.GetResponse())
                System.Windows.Forms.Application.DoEvents();
            }

            return (i == fileContents.Length);

        }

        /// <summary>
        /// Returns the full URL of the file on the FTP server
        /// </summary>
        /// <returns>URL</returns>
        private string build_URL()
        {
            string returnVal = ftp_Path();
            returnVal += ftp_File_Name();
            return returnVal;
        }

        /// <summary>
        /// Gets the length of a file from the FTP server
        /// </summary>
        /// <returns></returns>
        private long get_File_Length()
        {
            long returnVal = 0;
            FtpWebRequest ftp = (FtpWebRequest)WebRequest.Create(new Uri(build_URL()));
            ftp.Credentials = new NetworkCredential(m_Username, m_Password);
            ftp.UseBinary = true;
            ftp.KeepAlive = false;
            ftp.Method = WebRequestMethods.Ftp.GetFileSize;
            using (WebResponse response = ftp.GetResponse())
            {
                Stream ftpStream = response.GetResponseStream();
                returnVal = response.ContentLength;

                ftpStream.Close();
                response.Close();
            }

            return returnVal;
        }

        /// <summary>
        /// Checks if the file exists on the ftp server
        /// </summary>
        /// <returns>true or false</returns>
        private bool file_Exists()
        {
            // file name
            string fileName = ftp_File_Name().ToLower();
            bool returnVal = false;
            try
            {

                FtpWebRequest ftp = (FtpWebRequest)WebRequest.Create(new Uri(ftp_Path()));
                ftp.Credentials = new NetworkCredential(m_Username, m_Password);
                ftp.UseBinary = true;
                ftp.KeepAlive = false;
                ftp.Method = WebRequestMethods.Ftp.ListDirectory;
                using (WebResponse response = ftp.GetResponse())
                {
                    using (StreamReader reader = new StreamReader(response.GetResponseStream()))
                    {
                        string line = reader.ReadLine();
                        while (line != null)
                        {
                            if (line.ToLower() == fileName) returnVal = true;
                            line = reader.ReadLine();
                        }

                        reader.Close();
                    }
                    response.Close();
                }

                // if you are here, you were successful
                m_RetryFileExists = 0;

            }
            catch
            {
                // if you are here, try again
                m_RetryFileExists++;
            }

            return returnVal;
            
        }

        /// <summary>
        /// Builds the path for the URI
        /// </summary>
        /// <returns>ftp://m_url/</returns>
        private string ftp_Path()
        {
            string path = m_URL;
            if (!path.ToLower().StartsWith("ftp:")) path = @"ftp://" + path;
            if (!path.EndsWith(@"/")) path += @"/";
            return path;
        }

        /// <summary>
        /// Gets just the file name from the full path
        /// </summary>
        /// <returns>File name</returns>
        private string ftp_File_Name()
        {
            FileInfo fi = new FileInfo(m_FullPath);
            return fi.Name;
        }

        private void rename_File()
        {
            FtpWebRequest ftp = (FtpWebRequest)WebRequest.Create(new Uri(build_URL()));
            ftp.Credentials = new NetworkCredential(m_Username, m_Password);

            ftp.Method = WebRequestMethods.Ftp.Rename;
            ftp.RenameTo = ftp_File_Name() + string.Format(".{0}", DateTime.Now.ToString("yyyyMMddhhmmss"));
            ftp.UseBinary = true;
            ftp.KeepAlive = false;
            FtpWebResponse response = (FtpWebResponse)ftp.GetResponse();
            Stream s = response.GetResponseStream();

            s.Close();
            response.Close();

        }

        private void Wait(int pSeconds)
        {
            DateTime dt = DateTime.Now;
            while (dt.AddMilliseconds(pSeconds * 1000) > DateTime.Now)
            {
                System.Windows.Forms.Application.DoEvents();
            }
        }
    }

}
